#ifndef _DRAW_SYSTEM_H_
#define _DRAW_SYSTEM_H_

#include <set>
#include <map>
#include <pthread.h>

#include "../tools/Point.h"
#include "../LogicSystem.h"

using namespace std;

//Forward declaration to reduce dependencies
class OrthoObject;
class ModelObject;

/**	System that handles all management of drawing
*/
class DrawSystem: public LogicSystem
{
	set<OrthoObject*> orthoObjects;		//All 2D objects to be drawn
	set<ModelObject*> modelObjects;		//All 3D objects to be drawn
		
	fVec3D cameraUp;					//The up vector for gluLookAt		TODO consider removing and hardcoding up?
	
	int windowWidth, windowHeight;		//The width and height of the window in number of pixels
	float frameWidth, frameHeight;		//The width and height of the generated 2D frame width = aspectRatio*frameHeight, height = 100
	
	double FoV;						//The vertical angle field of view to use
	double aspectRatio;					//The current window aspect ratio
	
	void setOrthoView();				//Set the drawing mode to 2D (ortho)
	void setModelView();				//Set the drawing mode to 3D (model)
	
	//To be called whenever the window resizes
	friend void resizeWindow(int width, int height);
	
	//Singleton, no one can make one of these except us
	DrawSystem();
	DrawSystem(const DrawSystem&);
	DrawSystem& operator = (const DrawSystem&){ return *this; };
	
public:
	static inline DrawSystem& Instance() {	static DrawSystem ds; return ds;	}
	
	// Predefined depths to use for 2D, so you can put something behind, in the middle, or in front of other things
	enum DRAW_DEPTH {
		BACKGROUND = 30,
		MIDDLEGROUND = 20,
		FOREGROUND = 10
	};
	
	void update(long dt);				// Renders all objects
	bool init(const CmdLineArgs& args);
	void kill();
	void terminate();
	
	// Interface for adding and removing 2D objects
	bool addOrthoObject(OrthoObject* o);
	bool removeOrthoObject(OrthoObject* o);
	
	// Interface for adding and removing 3D objects
	bool addModelObject(ModelObject* o);
	bool removeModelObject(ModelObject* o);
	
	//The camera's position and speed in units per millisecond. Both can be edited by anyone. Position is automatically updated via speed and update's dt
	// cameraPosSpeed treats x as forward and y was sideways
	fVec3D cameraPosition, cameraPosSpeed;
	//The camera's look at position and speed. Works similar to position and position speed above
	// cameraRotSpeed is only affected by the y value
	fVec3D cameraLookAt, cameraRotSpeed;
	
	//Accessors
	inline double getFOV() { return FoV; }
	inline double getAspectRatio() { return aspectRatio; }
	inline int getWindowWidth() { return windowWidth; }
	inline int getWindowHeight() { return windowHeight; }
	inline float getFrameWidth() { return frameWidth; }
	inline float getFrameHeight() { return frameHeight; }
};

#endif
